/* Microsoft Reference Implementation for TPM 2.0
 *
 *  The copyright in this software is being made available under the BSD License,
 *  included below. This software may be subject to other third party and
 *  contributor rights, including patent rights, and no such rights are granted
 *  under this license.
 *
 *  Copyright (c) Microsoft Corporation
 *
 *  All rights reserved.
 *
 *  BSD License
 *
 *  Redistribution and use in source and binary forms, with or without modification,
 *  are permitted provided that the following conditions are met:
 *
 *  Redistributions of source code must retain the above copyright notice, this list
 *  of conditions and the following disclaimer.
 *
 *  Redistributions in binary form must reproduce the above copyright notice, this
 *  list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ""AS IS""
 *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 *  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 *  ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 *  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 *  ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
#include "Tpm.h"
#include "PCR_Extend_fp.h"

#if CC_PCR_Extend  // Conditional expansion of this file

/*(See part 3 specification)
// Update PCR
*/
//  Return Type: TPM_RC
//      TPM_RC_LOCALITY             current command locality is not allowed to
//                                  extend the PCR referenced by 'pcrHandle'
/*
Spec3 22.2 TPM2_PCR_Extend

This command is used to cause an update to the indicated PCR. The digests parameter contains one or more tagged digest values identified by an algorithm ID. 
For each digest, the PCR associated with pcrHandle is Extended into the bank identified by the tag (hashAlg).
EXAMPLE A SHA1 digest would be Extended into the SHA1 bank and a SHA256 digest would be Extended into the SHA256 bank.
For each list entry, the TPM will check to see if pcrNum is implemented for that algorithm. If so, the TPM shall perform the following operation:

该命令用于引起对指示的 PCR 的更新。 digests 参数包含一个或多个由算法 ID 标识的标记摘要值。
对于每个摘要，与 pcrHandle 关联的 PCR 被扩展到由标签 (hashAlg) 标识的库中。
示例 SHA1 摘要将扩展到 SHA1 库中，而 SHA256 摘要将扩展到 SHA256 库中。
对于每个列表条目，TPM 将检查是否为该算法实现了 pcrNum。 如果是这样，TPM 应执行以下操作：
PCR.digestnew [pcrNum][alg] ≔ Halg(PCR.digestold [pcrNum][alg] || data[alg].buffer))

If no digest value is specified for a bank, then the PCR in that bank is not modified.
NOTE 1 This allows consistent operation of the digests list for all of the Event recording commands.
If a digest is present and the PCR in that bank is not implemented, the digest value is not used.
NOTE 2 If the caller includes digests for algorithms that are not implemented, then the TPM will fail 
the call because the unmarshalling of digests will fail. Each of the entries in the list is a TPMT_HA, 
which is a hash algorithm followed by a digest. If the algorithm is not implemented, unmarshalling of 
the hashAlg will fail and the TPM will return TPM_RC_HASH.
如果没有为银行指定摘要值，则不会修改该银行中的 PCR。
注 1 这允许对所有事件记录命令的摘要列表进行一致的操作。
如果存在摘要并且未实施该库中的 PCR，则不使用摘要值。
注 2 如果调用者包含未实现算法的摘要，则 TPM 将导致调用失败，因为摘要的解组将失败。 列表中的每个条目都是一个 TPMT_HA，它是一种散列算法，
后跟摘要。 如果该算法未实现，则 hashAlg 的解组将失败并且 TPM 将返回 TPM_RC_HASH。

If the TPM unmarshals the hashAlg of a list entry and the unmarshaled value is not a hash algorithm 
implemented on the TPM, the TPM shall return TPM_RC_HASH.
The pcrHandle parameter is allowed to reference TPM_RH_NULL. If so, the input parameters are processed 
but no action is taken by the TPM. This permits the caller to probe for implemented hash algorithms as 
an alternative to TPM2_GetCapability.

NOTE 3 This command allows a list of digests so that PCR in all banks may be updated in a single command. 
While the semantics of this command allow multiple extends to a single PCR bank, this is not the preferred 
use and the limit on the number of entries in the list make this use somewhat impractical.
如果 TPM 解组列表条目的 hashAlg 并且解组值不是在 TPM 上实现的哈希算法，则 TPM 应返回 TPM_RC_HASH。
pcrHandle 参数允许引用 TPM_RH_NULL。 如果是，则处理输入参数但 TPM 不采取任何操作。 这允许调用者探测已实现的哈希算法作为 
TPM2_GetCapability 的替代方法。

注 3 此命令允许摘要列表，以便所有组中的 PCR 可以在单个命令中更新。 虽然此命令的语义允许对单个 PCR 库进行多次扩展，但这不是首选用途，
并且列表中条目数的限制使此用途有些不切实际。
*/
TPM_RC
TPM2_PCR_Extend(
    PCR_Extend_In   *in             // IN: input parameter list
    )
{
    UINT32              i;

// Input Validation

    // NOTE: This function assumes that the unmarshaling function for 'digests' will
    // have validated that all of the indicated hash algorithms are valid. If the
    // hash algorithms are correct, the unmarshaling code will unmarshal a digest
    // of the size indicated by the hash algorithm. If the overall size is not
    // consistent, the unmarshaling code will run out of input data or have input
    // data left over. In either case, it will cause an unmarshaling error and this
    // function will not be called.

    // For NULL handle, do nothing and return success
    if(in->pcrHandle == TPM_RH_NULL)
        return TPM_RC_SUCCESS;

    // Check if the extend operation is allowed by the current command locality
    if(!PCRIsExtendAllowed(in->pcrHandle))
        return TPM_RC_LOCALITY;

    // If PCR is state saved and we need to update orderlyState, check NV
    // availability
    if(PCRIsStateSaved(in->pcrHandle))
        RETURN_IF_ORDERLY;

// Internal Data Update

    // Iterate input digest list to extend
    for(i = 0; i < in->digests.count; i++)
    {
        PCRExtend(in->pcrHandle, in->digests.digests[i].hashAlg,
                  CryptHashGetDigestSize(in->digests.digests[i].hashAlg),
                  (BYTE *)&in->digests.digests[i].digest);
    }

    return TPM_RC_SUCCESS;
}

#endif // CC_PCR_Extend